package com.gaojinqi.base.common.util;


import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

/**
 * 反射工具类
 *
 * @author gaojinqi
 * @version 1.0
 * @since 2020年04月10日
 */
public class FieldUtil {

    // 字段过滤器，可组合使用
    /**
     * 静态字段过滤器
     */
    public static final Predicate<Field> STATIC_FILTER = field -> Modifier.isStatic(field.getModifiers());
    /**
     * 非静态字段过滤器
     */
    public static final Predicate<Field> NON_STATIC_FILTER = STATIC_FILTER.negate();
    /**
     * public字段过滤器
     */
    public static final Predicate<Field> PUBLIC_FILTER = field -> Modifier.isPublic(field.getModifiers());
    /**
     * protected字段过滤器
     */
    public static final Predicate<Field> PROTECTED_FILTER = field -> Modifier.isProtected(field.getModifiers());
    /**
     * private字段过滤器
     */
    public static final Predicate<Field> PRIVATE_FILTER = field -> Modifier.isPrivate(field.getModifiers());
    /**
     * abstract字段过滤器
     */
    public static final Predicate<Field> ABSTRACT_FILTER = field -> Modifier.isAbstract(field.getModifiers());
    /**
     * final字段过滤器
     */
    public static final Predicate<Field> FINAL_FILTER = field -> Modifier.isFinal(field.getModifiers());
    /**
     * native字段过滤器
     */
    public static final Predicate<Field> NATIVE_FILTER = field -> Modifier.isNative(field.getModifiers());
    /**
     * volatile字段过滤器
     */
    public static final Predicate<Field> VOLATILE_FILTER = field -> Modifier.isVolatile(field.getModifiers());
    /**
     * transient字段过滤器
     */
    public static final Predicate<Field> TRANSIENT_FILTER = field -> Modifier.isTransient(field.getModifiers());
    /**
     * 字段缓存
     */
    private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentHashMap<>();

    /**
     * 查找指定类中的所有字段（包括非public字段），也包括父类和Object类的字段， 字段不存在则返回<code>null</code>
     *
     * @param beanClass 被查找字段的类,不能为null
     * @param name      字段名
     * @return 指定字段名的字段，找不到则返回null
     */
    public static Field getField(Class<?> beanClass, String name) {
        final Field[] fields = getFields(beanClass);
        if (null != fields && fields.length != 0) {
            for (Field field : fields) {
                if ((name.equals(field.getName()))) {
                    return field;
                }
            }
        }
        return null;
    }

    /**
     * 获得一个类中所有非静态字段列表，包括其父类中的字段
     *
     * @param beanClass 类
     * @return 字段列表
     */
    public static Field[] getNonStaticFields(Class<?> beanClass) {
        return getFieldsByFilter(beanClass, NON_STATIC_FILTER);
    }

    /**
     * 获得一个类中所有静态字段列表，包括其父类中的字段
     *
     * @param beanClass 类
     * @return 字段列表
     */
    public static Field[] getStaticFields(Class<?> beanClass) {
        return getFieldsByFilter(beanClass, STATIC_FILTER);
    }

    /**
     * 根据过滤器获得一个类中字段列表，包括其父类中的字段
     *
     * @param beanClass 类
     * @param filter    字段过滤器
     * @return 字段列表
     */
    public static Field[] getFieldsByFilter(Class<?> beanClass, Predicate<Field> filter) {
        Field[] fields = getFields(beanClass);
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            if (filter.test(field)) {
                fieldList.add(field);
            }
        }
        return fieldList.toArray(new Field[0]);
    }

    /**
     * 获得一个类中所有字段列表，包括其父类中的字段
     *
     * @param beanClass 类
     * @return 字段列表
     */
    public static Field[] getFields(Class<?> beanClass) {
        Field[] allFields = FIELDS_CACHE.get(beanClass);
        if (null != allFields) {
            return allFields;
        }
        allFields = getFieldsDirectly(beanClass, true);
        FIELDS_CACHE.put(beanClass, allFields);
        return allFields;
    }

    /**
     * 获得一个类中所有非静态字段列表，直接反射获取，不使用缓存
     *
     * @param beanClass           类
     * @param withSuperClassFieds 是否要包括父类的字段列表
     * @return 字段列表
     */
    public static Field[] getNonStaticFieldsDirectly(Class<?> beanClass, boolean withSuperClassFieds) {
        return getFieldsDirectlyByFilter(beanClass, withSuperClassFieds, NON_STATIC_FILTER);
    }

    /**
     * 获得一个类中所有静态字段列表，直接反射获取，不使用缓存
     *
     * @param beanClass           类
     * @param withSuperClassFieds 是否要包括父类的字段列表
     * @return 字段列表
     */
    public static Field[] getStaticFieldsDirectly(Class<?> beanClass, boolean withSuperClassFieds) {
        return getFieldsDirectlyByFilter(beanClass, withSuperClassFieds, STATIC_FILTER);
    }

    /**
     * 根据过滤器获得一个类中字段列表，直接反射获取，不使用缓存
     *
     * @param beanClass           类
     * @param withSuperClassFieds 是否要包括父类的字段列表
     * @param filter              字段过滤器
     * @return 字段列表
     */
    public static Field[] getFieldsDirectlyByFilter(Class<?> beanClass, boolean withSuperClassFieds, Predicate<Field> filter) {
        Field[] fields = getFieldsDirectly(beanClass, withSuperClassFieds);
        List<Field> fieldList = new ArrayList<>();
        for (Field field : fields) {
            if (filter.test(field)) {
                fieldList.add(field);
            }
        }
        return fieldList.toArray(new Field[0]);
    }


    /**
     * 获得一个类中所有字段列表，直接反射获取，不使用缓存
     *
     * @param beanClass           类
     * @param withSuperClassFieds 是否要包括父类的字段列表
     * @return 字段列表
     */
    public static Field[] getFieldsDirectly(Class<?> beanClass, boolean withSuperClassFieds) {
        Field[] allFields = null;
        Class<?> searchClass = beanClass;
        Field[] declaredFields;
        while (searchClass != null) {
            declaredFields = searchClass.getDeclaredFields();
            if (null == allFields) {
                allFields = declaredFields;
            } else {
                // 复制数组
                int oldPos = allFields.length;
                int addLength = declaredFields.length;
                allFields = Arrays.copyOf(allFields, oldPos + addLength);
                System.arraycopy(declaredFields, 0, allFields, oldPos, addLength);
            }
            searchClass = withSuperClassFieds ? searchClass.getSuperclass() : null;
        }
        return allFields;
    }

    /**
     * @param beanClass       类
     * @param annotationClass 字段上的注解
     * @param present         设置注解存在时取字段还是不存在时取
     * @return 将类上根据是否存在指定注解的字段取出来
     */
    public static List<Field> getFieldsWithAnnotation(
            Class<?> beanClass, Class<? extends Annotation> annotationClass, boolean present) {
        List<Field> filerFields = new ArrayList<>();
        Field[] fields = getFields(beanClass);
        if (fields != null) {
            for (Field field : fields) {
                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                if (present) {
                    if (field.isAnnotationPresent(annotationClass)) {
                        try {
                            filerFields.add(field);
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    if (!field.isAnnotationPresent(annotationClass)) {
                        try {
                            filerFields.add(field);
                        } catch (IllegalArgumentException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return filerFields;
    }

    /**
     * @param beanClass       类
     * @param annotationClass 字段上的注解
     * @return 将类上存在指定注解的字段取出来
     */
    public static List<Field> getFieldsWithAnnotationPresent(
            Class<?> beanClass, Class<? extends Annotation> annotationClass) {
        return getFieldsWithAnnotation(beanClass, annotationClass, true);
    }

    /**
     * @param beanClass       类
     * @param annotationClass 字段上的注解
     * @return 将类上不存在指定注解的字段取出来
     */
    public static List<Field> getFieldsWithAnnotationAbsent(
            Class<?> beanClass, Class<? extends Annotation> annotationClass) {
        return getFieldsWithAnnotation(beanClass, annotationClass, false);
    }

}
